/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * The software in this package is published under the terms of the GPL v2.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE-GPL.txt file.
 */

package org.cemantika.aspects;

import java.util.List;

import org.aspectj.lang.annotation.SuppressAjWarnings;
import org.cemantika.management.ContextManager;
import org.cemantika.management.drools.DroolsContextualManipulation;
import org.cemantika.metamodel.structure.ContextualElement;
import org.cemantika.metamodel.structure.ContextualEntity;
import org.cemantika.model.ContextConsumerModel;
import org.cemantika.util.CemantikaLogger;

/**
 * ContextualElementDissemination represents the aspect that updates the context consumer when a
 * {@link ContextualEntity} is updated.
 * 
 * @author andersonfellipe
 */
@SuppressWarnings("unchecked")
public aspect ContextualElementDissemination {

    /**
     * Represents the attribute with the {@link ContextualElement} annotation receiving a new value
     * into a {@link ContextualEntity} class.
     * 
     * @param contextualElement the object of the annotation {@link ContextualElement}.
     * @param obj the object of the {@link ContextualEntity} class.
     */
    pointcut contextualInformationDisemination(Object obj): 
        @within(ContextualEntity) &&
        this(obj) &&
        set(* *) && 
        @annotation(ContextualElement) && 
        !withincode((@ContextualEntity *).new(..));

    /**
     * Represents the update of the contextual element into the context consumers it is related with.
     * 
     * @param contextualElement the object of the annotation {@link ContextualElement}.
     */
    @SuppressWarnings("restriction")
    @SuppressAjWarnings( {"adviceDidNotMatch"})
    after(Object obj): contextualInformationDisemination(obj) {
        ContextManager contextManager = ContextManager.getInstance();
        String element = contextManager.getContextualElementAcquired();
        String contextualEntitySuperclassType = obj.getClass().getSuperclass().getSimpleName();
        int generalizationCount = 0;
        while (generalizationCount < 3) {
            if (element == null) {
                element = obj.getClass().getSimpleName();
                element = element + "." + thisJoinPoint.getSignature().getName();
            } else if (!contextualEntitySuperclassType.equals("Object")) {
                if (generalizationCount == 1) {
                    element = obj.getClass().getSuperclass().getSimpleName();
                    element = element + "." + thisJoinPoint.getSignature().getName();
                } else if (generalizationCount == 2) {
                    element = obj.getClass().getSuperclass().getSuperclass().getSimpleName();
                    element = element + "." + thisJoinPoint.getSignature().getName();
                }
            } else {
                generalizationCount = 3;
            }
            List<ContextConsumerModel> contextConsumers = contextManager.getContextConsumersOfElement(element);
            if (!contextConsumers.isEmpty()) {
                for (ContextConsumerModel contextConsumer : contextConsumers) {
                    if (contextManager.isContextualEntityOfFocus(contextConsumer, obj)) {
                        contextConsumer.getFocus().updateContextualEntity(obj);
                        DroolsContextualManipulation drools = contextManager.searchDroolsContextualManipulation(contextConsumer.getFocus());
                        contextManager.updateFocus(contextConsumer.getFocus(), drools.getContextUser());
                        CemantikaLogger.logger.info("** Updating context consumer "
                                + contextConsumer.getFocus().getClass().getSimpleName() + " for contextual element: "
                                + element);
                        break;
                    }
                }
            }
            generalizationCount++;
        }
    }

}
